#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#include <Wire.h>
#include <Ticker.h>
#include "si5351.h"

#define SERVICE_UUID "39d585b9-cbe1-49bc-953a-83c3030ce1c7"
#define CHARACTERISTIC_UUID "0cda56ad-5258-47c4-8474-3176c307e091"

const char PIN_LED = 12;
const char PIN_SDA = 4;
const char PIN_SCL = 5;
Ticker ticker;
Si5351 si5351;

void tickerCallback()
{
  digitalWrite(PIN_LED, !digitalRead(PIN_LED));
}

void blinkLed()
{
  ticker.attach_ms(500, tickerCallback);
}

void lightLed()
{
  ticker.detach();
  digitalWrite(PIN_LED, HIGH);
}

class ServerCallbacks : public BLEServerCallbacks
{
  void onConnect(BLEServer *pServer)
  {
    lightLed();
  };
  void onDisconnect(BLEServer *pServer)
  {
    blinkLed();
    pServer->startAdvertising();
  }
};

class CharacteristicCallbacks : public BLECharacteristicCallbacks
{
  void onWrite(BLECharacteristic *pCharacteristic)
  {
    uint8_t *data = pCharacteristic->getData();
    uint8_t clk = data[0] - '0';
    uint8_t separator = data[1];
    uint64_t freq = atoi((char *)data + 2);
    // 检查
    if (clk >= 0 && clk <= 2 && separator == '-' && freq >= 4 && freq <= 160 * 1000)
    {
      si5351.output_enable((si5351_clock)clk, 1);
      uint8_t r = si5351.set_freq(freq * 1000 * 100, (si5351_clock)clk);
      r += '0';
      pCharacteristic->setValue(&r, 1);
    }
    else
    {
      si5351.output_enable((si5351_clock)clk, 0);
      uint8_t r = '1';
      pCharacteristic->setValue(&r, 1);
    }
    // notify
    pCharacteristic->notify();
  }
};

void setup()
{
  Wire.setPins(PIN_SDA, PIN_SCL);
  pinMode(PIN_LED, OUTPUT);
  blinkLed();
  BLEDevice::init("SI5351-BLE-Server");
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new ServerCallbacks());
  BLEService *pService = pServer->createService(SERVICE_UUID);

  BLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY);
  pCharacteristic->addDescriptor(new BLE2902());
  pCharacteristic->setCallbacks(new CharacteristicCallbacks());

  pService->start();
  pServer->getAdvertising()->start();

  Serial.begin(115200);
  if (!si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0))
  {
    Serial.println("Device not found on I2C bus!");
    delay(500);
  }
  si5351.set_freq(10 * 1000 * 100, SI5351_CLK0);
}

void loop()
{
  delay(500);
}